package rs.etf.mips.crossroad.logic;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * Represents track in the street.
 * 
 * @author Momcilo
 * 
 */
public class Line implements ITrafficSpace {

	/**
	 * default shortest distance between the vehicles that is allowed.
	 */
private static double defaultDistance = 10;

	/**
	 * shortest distance between the vehicles that is allowed.
	 */
	private double distance;
	private double speed;
	private double length, width;

	private double pedestrianZoneWidth;

	private List<Vehicle> vehicles;

	private Light semaphore;

	/**
	 * the space where the vehicle moves into after leaving this traffic space
	 */
	private ITrafficSpace neighborTrafficSpace;

	/**
	 * Creates the new line with using specified parameters.
	 * 
	 * @param length
	 * @param width
	 */
	public Line(double length, double width, double speed) {
		this(length, width, speed, Line.defaultDistance, Light.Red, null);
	}

	/**
	 * Creates the new line with using specified parameters.
	 * 
	 * @param length
	 * @param width
	 * @param distance
	 * @param semaphore
	 * @param neighbor
	 */
	public Line(double length, double width, double speed, double distance,
			Light semaphore, ITrafficSpace neighbor) {
		super();
		this.length = length;
		this.width = width;
		this.setSpeed(speed);
		this.setDistance(distance);
		this.semaphore = semaphore;
		this.pedestrianZoneWidth = 82;
		this.setNeighborTrafficSpace(neighbor);
		this.vehicles = new ArrayList<Vehicle>();
	}

	/**
	 * Gets the neighbor traffic space where the vehicles will go after leaving
	 * this line.
	 * 
	 * @return <b>null</b> if there is no neighbor space; otherwise, connected
	 *         neighbor space.
	 */
	public ITrafficSpace getNeighborTrafficSpace() {
		return neighborTrafficSpace;
	}

	/**
	 * Sets the neighbor traffic space where the vehicles will go after leaving
	 * this line.
	 * 
	 * @param neighborTrafficSpace
	 *            - neighbor space
	 */
	public void setNeighborTrafficSpace(ITrafficSpace neighborTrafficSpace) {
		this.neighborTrafficSpace = neighborTrafficSpace;
	}

	/**
	 * Gets the shortest allowed space between the vehicles in the line.
	 * 
	 * @return the shortest allowed space
	 */
	public double getDistance() {
		return distance;
	}

	/**
	 * Sets the shortest allowed space between the vehicles in the line.
	 * 
	 * @param distance
	 *            - the value for the shortest allowed space
	 */
	public void setDistance(double distance) {
		this.distance = distance;
	}

	/**
	 * Gets the speed of the line.
	 * 
	 * @return the speed, v<sub>x</sub>
	 */
	public double getSpeed() {
		return speed;
	}

	/**
	 * Sets the speed of the line.
	 * 
	 * @return value of the speed, v<sub>x</sub>
	 */
	public void setSpeed(double speed) {
		this.speed = speed;
	}

	/**
	 * @return the length of the line.
	 */
	public double getLength() {
		return length;
	}

	/**
	 * @return the width of the line.
	 */
	public double getWidth() {
		return width;
	}

	public double getPedestrianZoneWidth() {
		return pedestrianZoneWidth;
	}

	public void setPedestrianZoneWidth(double pedestrianZoneWidth) {
		this.pedestrianZoneWidth = pedestrianZoneWidth;
	}

	/**
	 * Gets the light of the semaphore
	 * 
	 * @return <b>null</b> if there is no semaphore in the line
	 */
	public Light getSemaphore() {
		return semaphore;
	}

	public void setSemaphore(Light semaphore) {
		this.semaphore = semaphore;
	}

	@Override
	public boolean addVehicle(Vehicle vehicle, ITrafficSpace neighbor) {
		if (!isFree(0, 0))
			return false;
		vehicle.setParameters(0, this.width / 2, this.speed, 0, this);
		vehicles.add(vehicle);
		return true;
	}

	public boolean addVehicle(Vehicle vehicle) {
		return this.addVehicle(vehicle, null);
	}

	public Iterator<Vehicle> iterator() {
		return vehicles.iterator();
	}

	@Override
	public boolean move(Vehicle vehicle) {
		// XXX Add alignment when can move for less than dX or dY.

		if (!canMove(vehicle))
			return false;

		double x = vehicle.getX();
		double y = vehicle.getY();

		vehicle.setX(vehicle.getX() + vehicle.getDx());
		if (vehicle.getX() > this.length) {
			if (neighborTrafficSpace == null
					|| neighborTrafficSpace.addVehicle(vehicle, this)) {
				this.vehicles.remove(vehicle);
			} else {
				vehicle.setX(this.length);
			}
		}

		return (x != vehicle.getX()) || (y != vehicle.getY());
	}

	@Override
	public boolean canMove(Vehicle vehicle) {
		// XXX Possible problem when vehicle can move for less than getDx or
		// getDy.
		double x = vehicle.getX();
		double y = vehicle.getY();
		double xf = vehicle.getX() + vehicle.getDx();
		double yf = vehicle.getY() + vehicle.getDy();
		boolean free = (x >= 0)
				&& ((xf < this.length - this.pedestrianZoneWidth)
						|| x > this.length - this.pedestrianZoneWidth
						|| this.semaphore == null
						|| this.semaphore == Light.Green || this.semaphore == Light.Off)
				&& (yf >= 0) && (yf < this.width);
		Iterator<Vehicle> iter = this.vehicles.iterator();

		while (free && iter.hasNext()) {
			Vehicle v = iter.next();

			// vehicle can not collide with itself
			free &= (vehicle == v) || (distance(x, y, v) > this.distance);
		}

		return free;
	}

	@Override
	public boolean isFree(double x, double y) {
		// allow passing the end of the line when semaphore is not Red
		boolean free = (x >= 0)
				&& ((x < this.length) || this.semaphore == null || this.semaphore != Light.Red)
				&& (y >= 0) && (y < this.width);
		Iterator<Vehicle> iter = this.vehicles.iterator();

		while (free && iter.hasNext()) {
			Vehicle v = iter.next();

			free &= distance(x, y, v) > this.distance;
		}

		return free;
	}

	@Override
	public String toString() {
		StringBuffer line = new StringBuffer("Line [length=" + length
				+ ", width=" + width + ", semaphore=" + semaphore
				+ ", distance=" + distance + ", speed=" + speed
				+ ", neighboar=" + neighborTrafficSpace + "]");
		Iterator<Vehicle> iter = this.vehicles.iterator();

		while (iter.hasNext()) {
			line.append("\n\t" + iter.next().toString());
		}

		return line.toString();
	}

	@Override
	public void update() {
		for (int i = this.vehicles.size() - 1; i >= 0; i--) {
			this.vehicles.get(i).move();
		}
	}

	public List<Vehicle> getVehicle() {
		if (!vehicles.isEmpty())
			return this.vehicles;
		else
			return null;
	}

	private double distance(double x, double y, Vehicle v) {
		return Math.sqrt(Math.pow(x - v.getX(), 2) + Math.pow(y - v.getY(), 2));
	}

	/**
	 * <b>Testing purpose only!</b>
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		Line line = new Line(30, 5, 2);
		Vehicle v = new Vehicle();
		line.addVehicle(v);
		Vehicle u = new Vehicle();
		System.out.println("added: " + line.addVehicle(u) + "\n\n");

		for (int i = 0; i < 30; i++) {
			line.update();
			if (i == 7)
				line.addVehicle(u);
			System.out.println("iter: " + i + "\n\t" + line
					+ "\n-----------------\n\n");
		}
	}
}
